A fundamental property of debugging a conventional processor (e.g., microprocessor)-based system is the ability to halt execution at a specific instruction in the program code. The point at which execution is halted is commonly referred to as the breakpointed instruction. Breakpoints allow an associated debugger to examine the state of the system at the designated time, allowing, for example, software errors to be quickly identified and removed.
The debugger can be an external debugger connected to a debug port (in some conventional systems the port is can be implemented as a JTAG port, a particularly configured serial interface specified by the IEEE Joint Test Action Group), or the debugger can be a monitor program running on the processor's central processing unit (CPU). Such a monitoring program is commonly referred to as a self-hosted debugger. External debug tools are well suited for basic low-level, application-independent debugging. Self-hosted debuggers consume some system resources (e.g., program memory, communication peripherals, registers etc.), but are useful for higher-level debugging of complex applications, e.g. operating systems (OS), as they can provide OS-specific task and status information. Furthermore, self-hosted debuggers allow peripherals and interrupts to be serviced during the debug session, minimizing the impact to the underlying application. Both external and self-hosted debuggers can rely on the same debug features (e.g., breakpoints) in the CPU to enable the user to debug the program.
One conventional way to implement a breakpoint is to allocate a specific instruction opcode as a breakpoint instruction. When the CPU of the processor executes this software breakpoint, the CPU can enter debug mode, and return control to the debugger.
To insert a software breakpoint, conventionally the debugger may be required to store the opcode of the breakpointed instruction in some kind of internal, temporary storage. The program memory contents can then be changed, overwriting the opcode of the breakpointed instruction with the software breakpoint instruction. When the CPU executes the software breakpoint, the debugger can examine the system state, and optionally perform other debugging tasks. When finished with the debug tasks, the debugger must execute the breakpointed instruction and return to the instruction following the breakpoint. In some conventional systems, the breakpointed instruction is written to a debug instruction register, and executed on the CPU after it has returned from debug mode.
Software breakpoints are popular, as they are relatively inexpensive to implement, and a large number of breakpoints can be inserted. However, software breakpoints do have a few drawbacks, which limit their usefulness. Some instructions may depend on the mode in which they are executed, e.g. they may execute differently in debug mode than in normal mode. Other instructions, such as a branch instruction, may depend on the current program counter (PC) location as well. In certain implementations, the breakpointed instruction cannot be executed when the CPU is in debug mode, as described above. Instead, the debugger removes the software breakpoint by rewriting the original opcode back to its location in the program memory and exits debug mode, restarting the instruction. The original instruction is thus executed correctly, but the software breakpoint is lost, which is normally not desired. To prevent this, the debugger may be required to insert another software breakpoint on the following instruction before exiting debug mode. When returning from debug mode, the breakpointed instruction will be executed correctly, and the CPU will immediately return to debug mode on the following software breakpoint. The debugger can then overwrite the breakpointed instruction with the software breakpoint, remove the second breakpoint, and exit debug mode.
The multi-breakpoint process described above is sometimes used in connection with microcontrollers having volatile program memory (e.g. RAM). However, if non-volatile program memory such as Flash memory is used, the process becomes further complicated, since a large part of the memory may be required to be erased and rewritten to replace the breakpoint with the original opcode. The whole scale erase and re-write operations are a time-consuming procedure, and may only be applied a limited number of times before the Flash memory wears out. Software breakpoints in these types of systems and processes are less suitable for interactive debugging (e.g., on microcontroller devices with embedded Flash program memory).
For this reason, a number of hardware breakpoint modules are frequently found in such conventional systems. The hardware breakpoint modules do not alter the program memory, but instead halt the CPU when instructions at a particular address in the code are executed. In some conventional systems, each module can normally only halt at a single address. The hardware breakpoint modules may be very costly in terms of silicon area and normally only a relatively small number of hardware breakpoint modules may be included in a singular system. Therefore, hardware breakpoints are most useful to supplement software breakpoints, to reduce the number of times the Flash memory needs to be reprogrammed. In cases where the user needs more breakpoints than there are hardware breakpoint modules available, the debugger must again rely on software breakpoints, despite their inherent drawbacks.